﻿using System;
using OpenCVDemo.IService;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using OpenCvSharp;
using OpenCvSharp.Internal.Vectors;
using Sdcb.PaddleOCR.KnownModels;
using System.Net.Http;
using Sdcb.PaddleOCR;
using System.Threading.Tasks;

namespace OpenCVDemo.Service
{
    public class OpenCVSharpService : IOpenCVService
    {
        /// <summary>
        /// Hello
        /// </summary>
        public void Hello()
        {
            Mat srcImg = Cv2.ImRead("images/7.jpg");
            Cv2.ImShow("src", srcImg);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像操作入门
        /// </summary>
        public void BeginImage()
        {
            //输入(读取)
            Mat srcImg = Cv2.ImRead("images/7.jpg");

            //图像显示
            if (srcImg.Empty())
            {
                Console.WriteLine("未找到图像");
                return;
            }
            Cv2.ImShow("src", srcImg);
            Console.WriteLine("原图像的宽度:{0}px", srcImg.Cols);
            Console.WriteLine("原图像的高度:{0}px", srcImg.Rows);
            Console.WriteLine("原图像的通道数:{0}", srcImg.Channels());

            //图像处理，彩色转灰度图
            Mat grayImg = new Mat();
            Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
            Cv2.ImShow("gray", grayImg);

            //输出(保存)
            Cv2.ImWrite("images/7-gray.jpg", grayImg);

            //销毁资源
            Cv2.DestroyAllWindows();

            Cv2.WaitKey();
        }

        /// <summary>
        /// 视频摄像图操作
        /// </summary>
        public void VideoCapture()
        {
            VideoCapture videoCapture = new VideoCapture("videos/Video_2021-11-12_150456.wmv");
            if (!videoCapture.IsOpened())
            {
                Console.WriteLine("Open video failed!");
                return;
            }

            Mat frame = new Mat();
            while (true)
            {
                videoCapture.Read(frame);
                if (frame.Empty())
                {
                    Console.WriteLine("Video is empty!");
                    break;
                }

                Cv2.ImShow("video", frame);
                if (Cv2.WaitKey(30) > 0)//按任意键退出
                {
                    break;
                }
            }
        }

        /// <summary>
        /// 实践应用1
        /// </summary>
        public void PracticeCase1()
        {
            //1，视频分解图片            
            //VideoCapture videoCapture = new VideoCapture("./videos/2.avi");
            //if (!videoCapture.IsOpened())
            //{
            //    Console.WriteLine("Open video failed!");
            //    return;
            //}

            //Mat frame = new Mat();
            //string filename = "";
            //int i = 0;
            //while (true)
            //{
            //    videoCapture.Read(frame);
            //    if (frame.Empty())
            //    {
            //        Console.WriteLine("Video is empty!");
            //        break;
            //    }

            //    i++;
            //    filename = string.Format("./output/{0}.jpg", i);
            //    Cv2.ImShow("video", frame);
            //    Cv2.ImWrite(filename, frame);
            //    if (Cv2.WaitKey(30) > 0)//按任意键退出
            //    {
            //        break;
            //    }
            //}

            //2，批量读取图片
            //for (int i = 1; i <= 644; i++)
            //{
            //    string fileName = "./output/" + i.ToString() + ".jpg";
            //    Mat img = Cv2.ImRead(fileName);
            //    Cv2.ImShow("img", img);
            //    if (Cv2.WaitKey(30) > 0)//按任意键退出
            //    {
            //        break;
            //    }
            //}

            //3，图片合成视频
            VideoWriter videoWriter = new VideoWriter("1.avi", FourCC.MP42, 20, new Size(640, 480));
            Mat frame = new Mat();
            for (int i = 1; i <= 644; i++)
            {
                string fileName = "./output/" + i.ToString() + ".jpg";
                frame = Cv2.ImRead(fileName);
                videoWriter.Write(frame);
                if (Cv2.WaitKey(30) > 0)//按任意键退出
                {
                    break;
                }
            }
        }

        /// <summary>
        /// 认识Mat与颜色空间
        /// </summary>
        public void MatAndColor()
        {
            //初始化Mat
            Mat img1 = new Mat("./images/9.png");//等价于：Mat img1 = Cv2.ImRead("./images/car.jpg");

            //浅拷贝与深拷贝
            Mat img2 = img1;//浅拷贝
            Mat img3 = img1.Clone();//深拷贝

            //颜色转换
            Mat img4 = new Mat();
            Cv2.CvtColor(img1, img4, ColorConversionCodes.BGR2GRAY);
            Cv2.ImShow("img4", img4);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 绘图函数，主要用于识别结果的标记
        /// </summary>
        public void DrawFunction()
        {
            Mat img = new Mat(500, 500, MatType.CV_8UC3, new Scalar(0, 0, 0));

            //1，绘制直线
            Cv2.Line(img, new Point(10, 10), new Point(450, 450), new Scalar(0, 255, 0), 2);

            //2，绘制圆
            Cv2.Circle(img, new Point(250, 250), 50, new Scalar(0, 0, 255), -1);

            //3，绘制椭圆
            Cv2.Ellipse(img, new RotatedRect(new Point2f(250, 250), new Size2f(100, 200), 0), new Scalar(0, 255, 255), 2);

            //4，绘制矩形
            Cv2.Rectangle(img, new Rect(100, 100, 300, 200), new Scalar(255, 0, 0), 2);

            //5，添加文字
            Cv2.PutText(img, "Hello OpenCVSharp", new Point(10, 50), HersheyFonts.HersheyComplex, 1, new Scalar(0, 255, 255));

            //显示图像
            Cv2.ImShow("img", img);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 访问图像像素
        /// </summary>
        public void ImagePixel()
        {
            Mat img = Cv2.ImRead("images/1.jpg");

            //1，直接访问像素
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        if (i == j)
            //        {
            //            img.At<Vec3b>(i, j) = new Vec3b(0, 255, 0);//彩色图像
            //            //img.At<byte>(i, j) = 255;//灰度图像
            //        }
            //    }
            //}

            //3，实例：图像减色
            //byte reduceValue = 64; //减色系数(值越大输出颜色越少)
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        img.At<Vec3b>(i, j)[0] = (byte)((byte)((byte)(img.At<Vec3b>(i, j)[0] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //蓝色通道或灰度图像
            //        img.At<Vec3b>(i, j)[1] = (byte)((byte)((byte)(img.At<Vec3b>(i, j)[1] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //绿色通道
            //        img.At<Vec3b>(i, j)[2] = (byte)((byte)((byte)(img.At<Vec3b>(i, j)[2] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //红色通道
            //    }
            //}

            //4，实例：雪花效果
            int snowNum = 1000;//雪花或者椒盐噪声的数量
            Random random = new Random();
            for (int k = 0; k < snowNum; k++)
            {
                int i = random.Next() % img.Rows;
                int j = random.Next() % img.Cols;
                img.At<Vec3b>(i, j)[0] = 255;
                img.At<Vec3b>(i, j)[1] = 255;
                img.At<Vec3b>(i, j)[2] = 255;
            }

            //显示图像
            Cv2.ImShow("img", img);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像运算
        /// </summary>
        public void ImageOperation()
        {
            Mat img1 = Cv2.ImRead("images/4.jpg");
            Mat img2 = Cv2.ImRead("images/5.jpg");
            Mat dstImg = new Mat();

            //1，算术运算
            //加法
            //Cv2.Add(img1, img2, dstImg);
            //Cv2.AddWeighted(img1, 0.5, img2, 0.5, 0, dstImg);

            //减法
            //Cv2.Subtract(img1, img2, dstImg);
            //Cv2.Absdiff(img1, img2, dstImg);

            //Mat temp = new Mat(new Size(img1.Cols, img1.Rows), MatType.CV_8UC3);
            //temp.SetTo(new Scalar(1, 5, 1));
            //乘法
            //Cv2.Multiply(img1, temp, dstImg, 1);

            //除法
            //Cv2.Divide(img1, temp, dstImg, 1);

            //2，逻辑运算
            //与
            //Cv2.BitwiseAnd(img1, img2, dstImg);

            //或
            //Cv2.BitwiseOr(img1, img2, dstImg);

            //取反
            //Cv2.BitwiseNot(img1, dstImg);

            //异或
            Cv2.BitwiseXor(img1, img2, dstImg);

            //显示图像
            Cv2.ImShow("result", dstImg);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 对比度与亮度调整，通道分离
        /// </summary>
        public void ContrastBrightness()
        {
            //对比度与亮度调整
            //Mat img = Cv2.ImRead("images/6.jpg");
            //int contrast = 150;//对比度
            //int brightness = 0;//亮度
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        int B = (int)((contrast * 0.01) * img.At<Vec3b>(i, j)[0] + brightness);
            //        int G = (int)((contrast * 0.01) * img.At<Vec3b>(i, j)[1] + brightness);
            //        int R = (int)((contrast * 0.01) * img.At<Vec3b>(i, j)[2] + brightness);
            //        if (B > 255)
            //            B = 255;
            //        if (G > 255)
            //            G = 255;
            //        if (R > 255)
            //            R = 255;
            //        if (B < 0)
            //            B = 0;
            //        if (G < 0)
            //            G = 0;
            //        if (R < 0)
            //            R = 0;
            //        img.At<Vec3b>(i, j)[0] = (byte)B;
            //        img.At<Vec3b>(i, j)[1] = (byte)G;
            //        img.At<Vec3b>(i, j)[2] = (byte)R;
            //    }
            //}

            //通道分离与合并
            //分离
            Mat img = Cv2.ImRead("images/6.jpg");
            Mat[] Channels = img.Split();
            Cv2.ImShow("B", Channels[0]);
            Cv2.ImShow("G", Channels[1]);
            Cv2.ImShow("R", Channels[2]);

            //合并
            Mat dst = new Mat();
            Mat[] vChannels = new Mat[3];
            Cv2.Threshold(Channels[0], Channels[0], 100, 255, ThresholdTypes.Binary);
            Cv2.Threshold(Channels[1], Channels[1], 100, 255, ThresholdTypes.Binary);
            Cv2.Threshold(Channels[2], Channels[2], 100, 255, ThresholdTypes.Binary);

            vChannels[0] = Channels[0];
            vChannels[1] = Channels[1];
            vChannels[2] = Channels[2];
            Cv2.Merge(vChannels, dst);
            Cv2.ImShow("merge", dst);


            //显示图像
            Cv2.ImShow("result", img);
            Cv2.WaitKey();
        }

        /// <summary>
        /// ROI与Mask掩码
        /// </summary>
        public void ROIMask()
        {
            //ROI区域的设置与保存
            //Mat img = Cv2.ImRead("images/7.jpg");
            //Cv2.ImShow("src", img);
            //Mat roi = new Mat(img, new Rect(206, 222, 494, 246));

            //Cv2.ImWrite("output/roi.jpg", roi);
            //Cv2.ImShow("roi", roi);

            //直接融合
            //Mat img = Cv2.ImRead("images/7.jpg");
            //Mat logo = Cv2.ImRead("images/8.jpg");
            //Cv2.ImShow("src", img);
            //Cv2.ImShow("logo", logo);

            //Mat roi = new Mat(img, new Rect(10, 10, logo.Cols, logo.Rows));
            //logo.CopyTo(roi);

            //Cv2.ImShow("roi", roi);
            //Cv2.ImShow("result", img);

            //使用Mask融合
            Mat img = Cv2.ImRead("images/7.jpg");
            Mat logo = Cv2.ImRead("images/8.jpg");
            Cv2.ImShow("src", img);
            Cv2.ImShow("logo", logo);

            Mat mask = new Mat();
            Cv2.CvtColor(logo, mask, ColorConversionCodes.BGR2GRAY);//彩色转灰度图
            Cv2.ImShow("gray", mask); ;

            Cv2.BitwiseNot(mask, mask);//逻辑与，目的是让背景变成全黑
            Cv2.ImShow("bitwise_not", mask);

            Cv2.Threshold(mask, mask, 100, 255, ThresholdTypes.Binary);//二值化，目的是让灰度值大于100的设为最大255，其它的值为0
            Cv2.ImShow("threshold", mask);

            Mat roi = new Mat(img, new Rect(10, 10, logo.Cols, logo.Rows));
            logo.CopyTo(roi, mask);

            Cv2.ImShow("roi", roi);
            Cv2.ImShow("result", img);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像几何变换
        /// </summary>
        public void ImageGeometricTransform()
        {
            //图像缩放
            //Mat srcImg = Cv2.ImRead("images/7.jpg");
            //Cv2.ImShow("src", srcImg);

            //Mat destImg = new Mat();
            //Cv2.Resize(srcImg, destImg, new Size(), 0.5, 0.5);
            //Cv2.ImShow("dest", destImg);

            //图像平移
            //1)不改变图像大小，信息会丢失
            //Mat srcImg = new Mat("images/7.jpg");
            //Cv2.ImShow("src", srcImg);

            //int xOffset = 100, yOffset = 100;
            //int dstRows = srcImg.Rows;
            //int dstCols = srcImg.Cols;
            //Mat dstImg = new Mat(new Size(dstCols, dstRows), MatType.CV_8UC3);
            //ImgTranslate(srcImg, dstImg, xOffset, yOffset);
            //Cv2.ImShow("dst", dstImg);

            //2)会改变图像大小，信息不丢失
            //Mat srcImg = new Mat("images/7.jpg");
            //Cv2.ImShow("src", srcImg);

            //int xOffset = 100, yOffset = 100;
            //int dstRows = srcImg.Rows + Math.Abs(yOffset);
            //int dstCols = srcImg.Cols + Math.Abs(xOffset);
            //Mat dstImg = new Mat(new Size(dstCols, dstRows), MatType.CV_8UC3);
            //ImgTranslate(srcImg, dstImg, xOffset, yOffset);
            //Cv2.ImShow("dst", dstImg);

            //图像旋转
            //Mat srcImg = new Mat("images/7.jpg");
            //Cv2.ImShow("src", srcImg);

            //Point2f center = new Point2f(srcImg.Cols / 2, srcImg.Rows / 2);
            //double angle = 45;
            //double scale = 1;

            //Mat rotateMat= Cv2.GetRotationMatrix2D(center, angle, scale);

            //Mat dstImg = new Mat();
            //Cv2.WarpAffine(srcImg, dstImg, rotateMat, new Size(srcImg.Cols, srcImg.Rows));
            //Cv2.ImShow("dst", dstImg);

            //转置镜像
            //Mat srcImg = new Mat("images/7.jpg");
            //Cv2.ImShow("src", srcImg);

            //Mat dstImg = new Mat();
            //Cv2.Transpose(srcImg, dstImg);
            ////CvInvoke.Flip(srcImg, dstImg, FlipType.Horizontal);
            ////CvInvoke.Flip(srcImg, dstImg, FlipType.Vertical);
            //Cv2.Flip(srcImg, dstImg, FlipMode.XY);

            //Cv2.ImShow("dst", dstImg);

            //重映射
            Mat srcImg = new Mat("images/7.jpg");
            Cv2.ImShow("src", srcImg);

            Mat xMatImg = new Mat(new Size(srcImg.Cols, srcImg.Rows), MatType.CV_32FC1);
            Mat yMatImg = new Mat(new Size(srcImg.Cols, srcImg.Rows), MatType.CV_32FC1);

            for (int i = 0; i < srcImg.Rows; i++)
            {
                for (int j = 0; j < srcImg.Cols; j++)
                {
                    xMatImg.At<float>(i, j) = (float)j;
                    yMatImg.At<float>(i, j) = (float)(i + 5 * Math.Sin(j / 10.0));
                }
            }

            Mat dstImg = new Mat();
            Cv2.Remap(srcImg, dstImg, xMatImg, yMatImg, InterpolationFlags.Area);
            Cv2.ImShow("dst", dstImg);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像滤波，用于图像预处理，去除噪声和使图像变得平滑
        /// </summary>
        public void ImageFilter()
        {
            //线性滤波：方框滤波，均值滤波，高斯滤波
            //非线性滤波：中值滤波，双边滤波
            Mat srcImg = new Mat("images/7.jpg");
            Cv2.ImShow("src", srcImg);

            Mat dstImg = new Mat();
            //Cv2.BoxFilter(srcImg, dstImg, MatType.CV_8UC3, new Size(3, 3), new Point(-1, -1), true);//方框滤波
            //CvInvoke.Blur(srcImg, dstImg, new Size(3, 3), new Point(-1, -1));//均值滤波
            Cv2.GaussianBlur(srcImg, dstImg, new Size(3, 3), 0, 0);//高斯滤波
            //Cv2.MedianBlur(srcImg, dstImg, 7);//中值滤波
            //CvInvoke.BilateralFilter(srcImg, dstImg, 9, 20.0, 5.0);//双边滤波
            Cv2.ImShow("dst", dstImg);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像阈值化，用于图像分割
        /// </summary>
        public void ImageThreshold()
        {
            Mat srcImg = Cv2.ImRead("images/102.png");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = new Mat();
            Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);//彩色转灰度图
            Cv2.ImShow("gray", grayImg);

            Cv2.Threshold(grayImg, grayImg, 220, 255, ThresholdTypes.BinaryInv);//阈值化
            //Cv2.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.BinaryInv, 11, 5);
            Cv2.ImShow("threshold", grayImg);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 图像膨胀与腐蚀
        /// </summary>
        public void DilateErode()
        {
            Mat srcImg = Cv2.ImRead("images/102.png");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = new Mat();
            Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);//彩色转灰度图
            Cv2.ImShow("gray", grayImg);

            Cv2.Threshold(grayImg, grayImg, 220, 255, ThresholdTypes.BinaryInv);//阈值化
            //Cv2.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.BinaryInv, 11, 5);
            Cv2.ImShow("threshold", grayImg);

            Mat dstImg = new Mat();
            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 1));
            Cv2.Dilate(grayImg, dstImg, element);
            Cv2.ImShow("dilate", dstImg);

            //腐蚀
            //CvInvoke.Erode(grayImg, dstImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            //CvInvoke.Imshow("erode", dstImg);

            //形态学其它操作，包括：开运算，闭运算，顶帽，黑帽，形态学梯度
            //CvInvoke.MorphologyEx(grayImg, dstImg, MorphOp.Dilate, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());//等价于CvInvoke.Dilate
            //CvInvoke.Imshow("morphologyEx", dstImg);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 边缘检测
        /// </summary>
        public void EdgeDetection()
        {
            //Canny边缘检测
            Mat srcImg = Cv2.ImRead("images/102.png");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = new Mat();
            Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);//彩色转灰度图
            Cv2.ImShow("gray", grayImg);

            Cv2.Threshold(grayImg, grayImg, 220, 255, ThresholdTypes.BinaryInv);//阈值化
            //Cv2.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.BinaryInv, 11, 5);
            Cv2.ImShow("threshold", grayImg);

            Cv2.Canny(grayImg, grayImg, 100, 220);
            Cv2.ImShow("canny", grayImg);

            //TODO:常用的还有：Sobel，Laplacian边缘检测

            Cv2.WaitKey();
        }

        /// <summary>
        /// 霍夫变换，一种特征提取技术，主要用来检测直线或圆
        /// </summary>
        public void HoughTransform()
        {
            //标准霍夫变换
            Mat srcImg = Cv2.ImRead("images/10.jpg");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = new Mat();
            Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
            Cv2.Canny(grayImg, grayImg, 30, 100);

            LineSegmentPolar[] lines = Cv2.HoughLines(grayImg, 1, Math.PI / 90, 200);
            for (int i = 0; i < lines.Length; i++)
            {
                float rho = lines[i].Rho, theta = lines[i].Theta;
                Point pt1 = new Point();
                Point pt2 = new Point();
                double a = Math.Cos(theta), b = Math.Sin(theta);
                double x0 = a * rho, y0 = b * rho;
                pt1.X = (int)Math.Round(x0 + 600 * (-b));
                pt1.Y = (int)Math.Round(y0 + 600 * a);
                pt2.X = (int)Math.Round(x0 - 600 * (-b));
                pt2.Y = (int)Math.Round(y0 - 600 * a);
                Cv2.Line(srcImg, pt1, pt2, new Scalar(0, 255, 0), 2);
            }
            Cv2.ImShow("dst", srcImg);

            //累积概率霍夫变换
            //Mat srcImg = CvInvoke.Imread("images/10.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Canny(grayImg, grayImg, 30, 200);

            //LineSegment2D[] lines = CvInvoke.HoughLinesP(grayImg, 1, Math.PI / 180, 30, 5, 10);
            //for (int i = 0; i < lines.Length; i++)
            //{
            //    Point pt1 = lines[i].P1;
            //    Point pt2 = lines[i].P2;
            //    CvInvoke.Line(srcImg, pt1, pt2, new MCvScalar(0, 255, 0), 2);
            //}
            //CvInvoke.Imshow("dst", srcImg);

            //霍夫圆变换
            //Mat srcImg = CvInvoke.Imread("images/6.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);

            //CircleF[] circles = CvInvoke.HoughCircles(grayImg, HoughModes.Gradient, 1, 80, 100, 30, 120, 200);
            //for (int i = 0; i < circles.Length; i++)
            //{
            //    Console.WriteLine("{0}", i);
            //    CvInvoke.Circle(srcImg, new Point((int)circles[i].Center.X, (int)circles[i].Center.Y), (int)circles[i].Radius, new MCvScalar(255, 255, 0), 5);
            //}
            //CvInvoke.Imshow("dst", srcImg);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 直方图及应用
        /// </summary>

        public void Hist()
        {
            //1，直方图计算
            //Mat srcImg = Cv2.ImRead("images/11.jpg");
            //Cv2.ImShow("src", srcImg);

            //Mat grayImg = srcImg.CvtColor(ColorConversionCodes.BGR2GRAY);
            //Cv2.ImShow("gray", grayImg);

            //Mat dHist = new Mat();
            //int[] channels = new int[] { 0 };
            //Rangef[] ranges = new Rangef[] { new Rangef(0, 256) };
            //int[] histSize = new int[] { 256 };
            //int dims = 1; //需要统计的特征数目(只统计灰度图单通道)
            //Mat[] vMatImgs = new Mat[] { grayImg };
            //Cv2.CalcHist(vMatImgs, channels, new Mat(), dHist, dims, histSize, ranges);

            //2，直方图均衡化，主要用来增强图像对比度
            Mat srcImg = Cv2.ImRead("images/11.jpg", ImreadModes.Grayscale);
            Cv2.ImShow("src", srcImg);

            Mat dstImg = new Mat();
            Cv2.EqualizeHist(srcImg, dstImg);
            Cv2.ImShow("dst", dstImg);

            //彩色图像分离三通道，分别对每个通道进行直方图均衡化，再进行通道合并
            //Mat srcImg = CvInvoke.Imread("images/11.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat[] Channels = srcImg.Split();
            //CvInvoke.Imshow("B", Channels[0]);
            //CvInvoke.Imshow("G", Channels[1]);
            //CvInvoke.Imshow("R", Channels[2]);

            //CvInvoke.EqualizeHist(Channels[0], Channels[0]);
            //CvInvoke.EqualizeHist(Channels[1], Channels[1]);
            //CvInvoke.EqualizeHist(Channels[2], Channels[2]);

            ////合并
            //Mat dst = new Mat();
            //VectorOfMat vChannels = new VectorOfMat();
            //vChannels.Push(Channels[0]);
            //vChannels.Push(Channels[1]);
            //vChannels.Push(Channels[2]);
            //CvInvoke.Merge(vChannels, dst);
            //CvInvoke.Imshow("dst", dst);

            //3，直方图对比
            //Mat srcImg = CvInvoke.Imread("images/11.jpg");
            //Mat srcImg2 = CvInvoke.Imread("images/11.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //Mat grayImg2 = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.CvtColor(srcImg2, grayImg2, ColorConversion.Bgr2Gray);

            //Mat dHist = new Mat();
            //Mat dHist2 = new Mat();
            //int[] channels = new int[] { 0 };
            //float[] ranges = new float[] { 0, 255 };
            //int[] histSize = new int[] { 256 };
            //VectorOfMat vMatImgs = new VectorOfMat();
            //vMatImgs.Push(grayImg);
            //VectorOfMat vMatImgs2 = new VectorOfMat();
            //vMatImgs2.Push(grayImg2);
            //CvInvoke.CalcHist(vMatImgs, channels, new Mat(), dHist, histSize, ranges, false);
            //CvInvoke.CalcHist(vMatImgs2, channels, new Mat(), dHist2, histSize, ranges, false);

            //double matchValue0 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Correl);
            //double matchValue1 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Chisqr);
            //double matchValue2 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Intersect);
            //double matchValue3 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Bhattacharyya);
            //Console.WriteLine("match0 = {0}\nmatch1 = {1}\nmatch2 = {2}\nmatch3 = {3}\n", matchValue0, matchValue1, matchValue2, matchValue3);

            //4，反向投影，主要用来在输入图像(大)中查找与特定图像(模板图像), 最匹配的点或者区域, 也就是定位模板图像出现在输入图像的位置
            //Mat srcImg = CvInvoke.Imread("images/14.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat hsvImg = new Mat();
            //CvInvoke.CvtColor(srcImg, hsvImg, ColorConversion.Bgr2Hsv);

            //Mat dHist = new Mat();
            //int[] channels = new int[] { 0 };
            //float[] ranges = new float[] { 0, 180 };
            //int[] histSize = new int[] { 20 };//组距(直方图柱子数量)
            //VectorOfMat vMatImgs = new VectorOfMat();
            //vMatImgs.Push(hsvImg);
            //CvInvoke.CalcHist(vMatImgs, channels, new Mat(), dHist, histSize, ranges, false);
            //CvInvoke.Normalize(dHist, dHist, 0, 255, NormType.MinMax);

            //Mat backProject = new Mat();
            //CvInvoke.CalcBackProject(vMatImgs, channels, dHist, backProject, ranges);
            //CvInvoke.Imshow("backproject", backProject);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 模板匹配
        /// </summary>
        public void MatchTemplate()
        {
            //单模板匹配
            //Mat srcImg = Cv2.ImRead("images/18.png");
            //Cv2.ImShow("src", srcImg);
            //Mat result = srcImg.Clone();

            //Mat tempImg = Cv2.ImRead("images/19.png");
            //int dstImg_rows = srcImg.Rows - tempImg.Rows + 1;
            //int dstImg_cols = srcImg.Cols - tempImg.Cols + 1;
            //Mat dstImg = new Mat(dstImg_rows, dstImg_cols,MatType.CV_32F, 1);
            //Cv2.MatchTemplate(srcImg, tempImg, dstImg, TemplateMatchModes.CCoeffNormed);
            //Cv2.ImShow("match", dstImg);

            ////矩阵归一化
            //CvInvoke.Normalize(dstImg, dstImg, 0, 1, NormType.MinMax, dstImg.Depth);

            //double minValue = 1000, maxValue = -1;
            //Point minLoc = new Point(0, 0);
            //Point maxLoc = new Point(0, 0);
            //CvInvoke.MinMaxLoc(dstImg, ref minValue, ref maxValue, ref minLoc, ref maxLoc);//寻找最值
            //CvInvoke.Rectangle(result, new Rectangle(maxLoc.X, maxLoc.Y, tempImg.Width, tempImg.Height),new MCvScalar(0, 255, 0), 2);//标出识别结果
            //CvInvoke.Imshow("result", result);

            //视频模板匹配


            //多模板匹配
            //Mat srcImg = Cv2.ImRead("images/16.png");
            //Cv2.ImShow("src", srcImg);
            //Mat result = srcImg.Clone();

            //Mat tempImg = Cv2.ImRead("images/17.png");
            //int dstImg_rows = srcImg.Rows - tempImg.Rows + 1;
            //int dstImg_cols = srcImg.Cols - tempImg.Cols + 1;
            //Mat dstImg = new Mat(dstImg_rows, dstImg_cols, MatType.CV_32F, 1);
            //Cv2.MatchTemplate(srcImg, tempImg, dstImg, TemplateMatchModes.CCoeffNormed);
            //Cv2.ImShow("match", dstImg);

            //Cv2.Normalize(dstImg, dstImg, 0, 1, NormTypes.MinMax, dstImg.Depth());
            //Image<Gray, Single> ImgMatch = dstImg.ToImage<Gray, Single>();
            //int count = 0;
            //int tempW = 0, tempH = 0;
            //for (int i = 0; i < dstImg_rows; i++)
            //{
            //    for (int j = 0; j < dstImg_cols; j++)
            //    {
            //        float matchValue = ImgMatch.Data[i, j, 0];
            //        if (matchValue >= 0.65 && (Math.Abs(j - tempW) > 10) && (Math.Abs(i - tempH) > 10))
            //        {
            //            count++;
            //            CvInvoke.Rectangle(result, new Rectangle(j, i, tempImg.Width, tempImg.Height), new MCvScalar(0, 255, 0), 2);
            //            tempW = j;
            //            tempH = i;
            //        }
            //    }
            //}
            //Console.WriteLine("{0}", count);
            //CvInvoke.Imshow("result", result);

            Cv2.WaitKey();
        }

        /// <summary>
        /// 查找和绘制轮廓
        /// </summary>

        public void FindDrawContours()
        {
            Mat srcImg = Cv2.ImRead("images/20220601.png");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = srcImg.CvtColor(ColorConversionCodes.BGR2GRAY);
            Cv2.ImShow("gray", grayImg);

            //二值化
            Cv2.Threshold(grayImg, grayImg, 220, 255, ThresholdTypes.BinaryInv);
            Cv2.ImShow("threshold", grayImg);

            //膨胀
            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 1));
            Cv2.Dilate(grayImg, grayImg, element);
            Cv2.ImShow("dilate", grayImg);

            //查找轮廓
            Point[][] contours = Cv2.FindContoursAsArray(grayImg, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
            Console.WriteLine("contours_num={0}", contours.Length);

            //绘制轮廓
            Cv2.DrawContours(srcImg, contours, -1, new Scalar(0, 0, 255), 2);

            Cv2.ImShow("result", srcImg);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 凸包，一般应用在物体识别，手势检测和边界检测
        /// </summary>
        public void ConvexHull()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 轮廓外接矩形
        /// </summary>

        public void ContoursBoundingRectangle()
        {
            Mat srcImg = Cv2.ImRead("images/20220606.png");
            Cv2.ImShow("src", srcImg);

            Mat grayImg = srcImg.CvtColor(ColorConversionCodes.BGR2GRAY);
            Cv2.ImShow("gray", grayImg);

            //二值化
            Cv2.Threshold(grayImg, grayImg, 220, 255, ThresholdTypes.BinaryInv);
            Cv2.ImShow("threshold", grayImg);

            //膨胀
            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 1));
            Cv2.Dilate(grayImg, grayImg, element);
            Cv2.ImShow("dilate", grayImg);

            //查找轮廓
            Point[][] contours = Cv2.FindContoursAsArray(grayImg, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

            //绘制轮廓，正外接矩形
            Rect[] rects = contours.Select(p => Cv2.BoundingRect(p)).Where(p => p.Width > 400 && p.Height > 100).ToArray();
            Console.WriteLine("rects_num={0}", rects.Length);
            for (int i = 0; i < rects.Length; i++)
            {
                Cv2.Rectangle(srcImg, rects[i], new Scalar(0, 0, 255), 2);
            }

            //var list = rects
            //    .Reverse()
            //    .Select(rect =>
            //    {
            //        int rowHeight = DetectTableRowHeight(grayImg, rect);
            //        Mat rowHeader = srcImg[rect.Top - rowHeight * 2, rect.Top - rowHeight, rect.Left, rect.Left + rect.Width / 3];
            //        Mat titleRow = srcImg[rect.Top - rowHeight, rect.Top, rect.Left, rect.Right];
            //        Mat body = srcImg[rect];
            //        Mat foot = srcImg[rect.Bottom, rect.Bottom + rowHeight, rect.Left, rect.Right];

            //        return new GuZhiBiaoTableDesc()
            //        {
            //            RowHeight = rowHeight,
            //            RowHeader = rowHeader,
            //            TitleRow = titleRow,
            //            Body = body,
            //            Foot = foot
            //        };
            //    }).ToList();

            Cv2.ImShow("result", srcImg);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 轮廓最小外接矩形
        /// </summary>
        public void ContoursMinAreaRect()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 轮廓最小外接圆
        /// </summary>
        public void ContoursEnclosingCircle()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 轮廓位置关系与轮廓匹配
        /// </summary>
        public void ContoursPointPolygon()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 滑块拼图验证码
        /// </summary>
        public void SliderVerificationCode()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 轮廓颜色识别
        /// </summary>
        public void ContoursColorRecognition()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 图像修补
        /// </summary>
        public void ImageInPaint()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// GrabCut和FloodFill图像分割算法
        /// </summary>
        public void GrabCutAndFloodFill()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// PaddleOCR入门
        /// </summary>
        public void BeginPaddleOCR()
        {
            OCRModel model = KnownOCRModel.PPOcrV2;
            //await model.EnsureAll();
            model.EnsureAll().Wait();

            //Detection and Recognition(All)
            //using (PaddleOcrAll all = new PaddleOcrAll(model.RootDirectory, model.KeyPath)
            //{
            //    AllowRotateDetection = true, /* 允许识别有角度的文字 */
            //    Enable180Classification = false, /* 允许识别旋转角度大于90度的文字 */
            //})
            //{
            //    using (Mat src2 = Cv2.ImRead("images/107.png", ImreadModes.Color))
            //    {
            //        PaddleOcrResult result = all.Run(src2);
            //        Console.WriteLine("Detected all texts: \n" + result.Text);
            //        foreach (PaddleOcrResultRegion region in result.Regions)
            //        {
            //            Console.WriteLine($"Text: {region.Text}, Score: {region.Score}, RectCenter: {region.Rect.Center}, RectSize:    {region.Rect.Size}, Angle: {region.Rect.Angle}");
            //        }
            //    }
            //}

            //Detection Only
            using (PaddleOcrDetector detector = new PaddleOcrDetector(model.DetectionDirectory))
            using (Mat src = Cv2.ImRead("images/107.png", ImreadModes.Color))
            {
                RotatedRect[] rects = detector.Run(src);
                using (Mat visualized = PaddleOcrDetector.Visualize(src, rects, Scalar.Red, thickness: 2))
                {
                    //string outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "output.jpg");
                    //Console.WriteLine("OutputFile: " + outputFile);
                    visualized.ImWrite("output/202201211050.jpg");
                }
            }
        }

        #region Private method

        /// <summary>
        /// 检测表格行高
        /// </summary>
        /// <param name="gray"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        private int DetectTableRowHeight(Mat gray, OpenCvSharp.Rect rect)
        {
            using Mat roi = gray[rect.Top, rect.Bottom, rect.Left, rect.Left + 150];
            using Mat binary = roi.Threshold(90, 255, ThresholdTypes.BinaryInv);
            using Mat dilated = new Mat();
            using (Mat ones = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(25, 10)))
            {
                Cv2.Dilate(binary, dilated, ones);
            }

            OpenCvSharp.Rect[] rects = dilated.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxSimple)
                .Select(x => Cv2.BoundingRect(x))
                .Where(x => x.Width > 30)
                .ToArray();
            double average = rects
                .Zip(rects.Skip(1), (First, Second) => (First, Second))
                .Select(x => Math.Abs((x.First.Bottom + x.First.Top) / 2 - (x.Second.Bottom + x.Second.Top) / 2))
                .Average();

            return (int)Math.Round(average);

            //return 44;
        }

        //图像平移实现函数
        public static void ImgTranslate(Mat srcImg, Mat dstImg, int xOffset, int yOffset)
        {
            for (int i = 0; i < srcImg.Rows; i++)
            {
                for (int j = 0; j < srcImg.Cols; j++)
                {
                    int x = j + xOffset;
                    int y = i + yOffset;
                    if (x >= 0 && x < dstImg.Cols && y >= 0 && y < dstImg.Rows)
                    {
                        dstImg.At<Vec3b>(y, x) = srcImg.At<Vec3b>(i, j);
                    }
                }
            }
        }
        #endregion
    }

    public class GuZhiBiaoTableDesc
    {
        public int RowHeight { get; set; }

        public Mat RowHeader { get; set; }

        public Mat TitleRow { get; set; }

        public Mat Body { get; set; }

        public Mat Foot { get; set; }

    }
}
